using System;
using System.Collections.Generic;
using UnityEngine;
using static UnityEngine.ParticleSystem;

public static class ParticleSystemHasher
{
    private const ulong FNV64Prime = 0x100000001b3;
    private const ulong FNV64Offset = 0xcbf29ce484222325;

    // 浮点数精度转换系数
    private const int FloatPrecision = 1000; // 保留3位小数精度

    public static ulong ComputeParticleHash(ParticleSystem ps)
    {
        ulong hash = FNV64Offset;

        AddToHash(ref hash, ps.useAutoRandomSeed);
        if (!ps.useAutoRandomSeed)
        {
            AddToHash(ref hash, ps.randomSeed);
        }
        AddToHash(ref hash, ps.time);

        // 主模块
        var main = ps.main;
        AddToHash(ref hash, main.duration);
        AddToHash(ref hash, main.loop);
        AddToHash(ref hash, main.prewarm);
        AddToHash(ref hash, main.startDelay);
        AddToHash(ref hash, main.startDelayMultiplier);
        AddToHash(ref hash, main.startLifetime);
        AddToHash(ref hash, main.startLifetimeMultiplier);
        AddToHash(ref hash, main.startSpeed);
        AddToHash(ref hash, main.startSpeedMultiplier);
        AddToHash(ref hash, main.startSize3D);
        AddToHash(ref hash, main.startSize);
        AddToHash(ref hash, main.startSizeMultiplier);
        AddToHash(ref hash, main.startSizeX);
        AddToHash(ref hash, main.startSizeXMultiplier);
        AddToHash(ref hash, main.startSizeY);
        AddToHash(ref hash, main.startSizeYMultiplier);
        AddToHash(ref hash, main.startSizeZ);
        AddToHash(ref hash, main.startSizeZMultiplier);
        AddToHash(ref hash, main.startRotation3D);
        AddToHash(ref hash, main.startRotation);
        AddToHash(ref hash, main.startRotationMultiplier);
        AddToHash(ref hash, main.startRotationX);
        AddToHash(ref hash, main.startRotationXMultiplier);
        AddToHash(ref hash, main.startRotationY);
        AddToHash(ref hash, main.startRotationYMultiplier);
        AddToHash(ref hash, main.startRotationZ);
        AddToHash(ref hash, main.startRotationZMultiplier);
        AddToHash(ref hash, main.flipRotation);
        AddToHash(ref hash, main.startColor);
        AddToHash(ref hash, main.gravityModifier);
        AddToHash(ref hash, main.gravityModifierMultiplier);
        AddToHash(ref hash, (int)main.simulationSpace);
        AddToHash(ref hash, main.simulationSpeed);
        AddToHash(ref hash, main.useUnscaledTime);
        AddToHash(ref hash, (int)main.scalingMode);
        AddToHash(ref hash, main.playOnAwake);
        AddToHash(ref hash, main.maxParticles);
        AddToHash(ref hash, (int)main.emitterVelocityMode);
        AddToHash(ref hash, (int)main.stopAction);
        AddToHash(ref hash, (int)main.ringBufferMode);
        AddToHash(ref hash, main.ringBufferLoopRange);
        AddToHash(ref hash, (int)main.cullingMode);

        // 发射模块
        var emission = ps.emission;
        HashModule(ref hash, 1, emission.enabled);
        if (emission.enabled)
        {
            AddToHash(ref hash, emission.rateOverTime);
            AddToHash(ref hash, emission.rateOverTimeMultiplier);
            AddToHash(ref hash, emission.rateOverDistance);
            AddToHash(ref hash, emission.rateOverDistanceMultiplier);
            AddToHash(ref hash, emission.burstCount);
            for (int i = 0; i < emission.burstCount; i++)
            {
                var burst = emission.GetBurst(i);
                AddToHash(ref hash, burst.time);
                AddToHash(ref hash, burst.count);
                AddToHash(ref hash, burst.cycleCount);
                AddToHash(ref hash, burst.repeatInterval);
                AddToHash(ref hash, burst.probability);
            }
        }

        // 形状模块
        var shape = ps.shape;
        HashModule(ref hash, 2, shape.enabled);
        if (shape.enabled)
        {
            AddToHash(ref hash, (int)shape.shapeType);
            AddToHash(ref hash, shape.randomDirectionAmount);
            AddToHash(ref hash, shape.randomPositionAmount);
            AddToHash(ref hash, shape.alignToDirection);
            AddToHash(ref hash, shape.radius);
            AddToHash(ref hash, (int)shape.radiusMode);
            AddToHash(ref hash, shape.radiusSpread);
            AddToHash(ref hash, shape.radiusSpeed);
            AddToHash(ref hash, shape.radiusSpeedMultiplier);
            AddToHash(ref hash, shape.radiusThickness);
            AddToHash(ref hash, shape.angle);
            AddToHash(ref hash, shape.length);
            AddToHash(ref hash, shape.boxThickness);
            AddToHash(ref hash, (int)shape.meshShapeType);
            AddToHash(ref hash, shape.mesh ? shape.mesh.GetInstanceID() : 0);
            AddToHash(ref hash, shape.meshRenderer ? shape.meshRenderer.GetInstanceID() : 0);
            AddToHash(ref hash, shape.skinnedMeshRenderer ? shape.skinnedMeshRenderer.GetInstanceID() : 0);
            AddToHash(ref hash, shape.sprite ? shape.sprite.GetInstanceID() : 0);
            AddToHash(ref hash, shape.spriteRenderer ? shape.spriteRenderer.GetInstanceID() : 0);
            AddToHash(ref hash, shape.useMeshMaterialIndex);
            AddToHash(ref hash, shape.meshMaterialIndex);
            AddToHash(ref hash, shape.useMeshColors);
            AddToHash(ref hash, shape.normalOffset);
            AddToHash(ref hash, (int)shape.meshSpawnMode);
            AddToHash(ref hash, shape.meshSpawnSpread);
            AddToHash(ref hash, shape.meshSpawnSpeed);
            AddToHash(ref hash, shape.meshSpawnSpeedMultiplier);
            AddToHash(ref hash, shape.arc);
            AddToHash(ref hash, (int)shape.arcMode);
            AddToHash(ref hash, shape.arcSpread);
            AddToHash(ref hash, shape.arcSpeed);
            AddToHash(ref hash, shape.arcSpeedMultiplier);
            AddToHash(ref hash, shape.donutRadius);
            AddToHash(ref hash, shape.position);
            AddToHash(ref hash, shape.rotation);
            AddToHash(ref hash, shape.scale);
            AddToHash(ref hash, shape.texture ? shape.texture.GetInstanceID() : 0);
            AddToHash(ref hash, (int)shape.textureClipChannel);
            AddToHash(ref hash, shape.textureClipThreshold);
            AddToHash(ref hash, shape.textureColorAffectsParticles);
            AddToHash(ref hash, shape.textureAlphaAffectsParticles);
            AddToHash(ref hash, shape.textureBilinearFiltering);
            AddToHash(ref hash, shape.textureUVChannel);
        }

        // 速度生命周期模块
        var velocityOverLifetime = ps.velocityOverLifetime;
        HashModule(ref hash, 3, velocityOverLifetime.enabled);
        if (velocityOverLifetime.enabled)
        {
            AddToHash(ref hash, velocityOverLifetime.x);
            AddToHash(ref hash, velocityOverLifetime.y);
            AddToHash(ref hash, velocityOverLifetime.z);
            AddToHash(ref hash, velocityOverLifetime.xMultiplier);
            AddToHash(ref hash, velocityOverLifetime.yMultiplier);
            AddToHash(ref hash, velocityOverLifetime.zMultiplier);
            AddToHash(ref hash, velocityOverLifetime.orbitalX);
            AddToHash(ref hash, velocityOverLifetime.orbitalY);
            AddToHash(ref hash, velocityOverLifetime.orbitalZ);
            AddToHash(ref hash, velocityOverLifetime.orbitalXMultiplier);
            AddToHash(ref hash, velocityOverLifetime.orbitalYMultiplier);
            AddToHash(ref hash, velocityOverLifetime.orbitalZMultiplier);
            AddToHash(ref hash, velocityOverLifetime.orbitalOffsetX);
            AddToHash(ref hash, velocityOverLifetime.orbitalOffsetY);
            AddToHash(ref hash, velocityOverLifetime.orbitalOffsetZ);
            AddToHash(ref hash, velocityOverLifetime.orbitalOffsetXMultiplier);
            AddToHash(ref hash, velocityOverLifetime.orbitalOffsetYMultiplier);
            AddToHash(ref hash, velocityOverLifetime.orbitalOffsetZMultiplier);
            AddToHash(ref hash, velocityOverLifetime.radial);
            AddToHash(ref hash, velocityOverLifetime.radialMultiplier);
            AddToHash(ref hash, velocityOverLifetime.speedModifier);
            AddToHash(ref hash, velocityOverLifetime.speedModifierMultiplier);
            AddToHash(ref hash, (int)velocityOverLifetime.space);
        }

        var limitVelocityOverLifetime = ps.limitVelocityOverLifetime;
        HashModule(ref hash, 4, limitVelocityOverLifetime.enabled);
        if (limitVelocityOverLifetime.enabled)
        {
            AddToHash(ref hash, limitVelocityOverLifetime.limitX);
            AddToHash(ref hash, limitVelocityOverLifetime.limitY);
            AddToHash(ref hash, limitVelocityOverLifetime.limitZ);
            AddToHash(ref hash, limitVelocityOverLifetime.limitXMultiplier);
            AddToHash(ref hash, limitVelocityOverLifetime.limitYMultiplier);
            AddToHash(ref hash, limitVelocityOverLifetime.limitZMultiplier);
            AddToHash(ref hash, limitVelocityOverLifetime.limit);
            AddToHash(ref hash, limitVelocityOverLifetime.limitMultiplier);
            AddToHash(ref hash, limitVelocityOverLifetime.dampen);
            AddToHash(ref hash, limitVelocityOverLifetime.separateAxes);
            AddToHash(ref hash, (int)limitVelocityOverLifetime.space);
            AddToHash(ref hash, limitVelocityOverLifetime.drag);
            AddToHash(ref hash, limitVelocityOverLifetime.dragMultiplier);
            AddToHash(ref hash, limitVelocityOverLifetime.multiplyDragByParticleSize);
            AddToHash(ref hash, limitVelocityOverLifetime.multiplyDragByParticleVelocity);
        }

        var inheritVelocity = ps.inheritVelocity;
        HashModule(ref hash, 5, inheritVelocity.enabled);
        if (inheritVelocity.enabled)
        {
            AddToHash(ref hash, (int)inheritVelocity.mode);
            AddToHash(ref hash, inheritVelocity.curve);
            AddToHash(ref hash, inheritVelocity.curveMultiplier);
        }

        var lifetimeByEmitterSpeed = ps.lifetimeByEmitterSpeed;
        HashModule(ref hash, 6, lifetimeByEmitterSpeed.enabled);
        if (lifetimeByEmitterSpeed.enabled)
        {
            AddToHash(ref hash, lifetimeByEmitterSpeed.curve);
            AddToHash(ref hash, lifetimeByEmitterSpeed.curveMultiplier);
            AddToHash(ref hash, lifetimeByEmitterSpeed.range);
        }

        var forceOverLifetime = ps.forceOverLifetime;
        HashModule(ref hash, 7, forceOverLifetime.enabled);
        if (forceOverLifetime.enabled)
        {
            AddToHash(ref hash, forceOverLifetime.x);
            AddToHash(ref hash, forceOverLifetime.y);
            AddToHash(ref hash, forceOverLifetime.z);
            AddToHash(ref hash, forceOverLifetime.xMultiplier);
            AddToHash(ref hash, forceOverLifetime.yMultiplier);
            AddToHash(ref hash, forceOverLifetime.zMultiplier);
            AddToHash(ref hash, (int)forceOverLifetime.space);
            AddToHash(ref hash, forceOverLifetime.randomized);
        }

        var colorOverLifetimeModule = ps.colorOverLifetime;
        HashModule(ref hash, 8, colorOverLifetimeModule.enabled);
        if (colorOverLifetimeModule.enabled)
        {
            AddToHash(ref hash, colorOverLifetimeModule.color);
        }

        var colorBySpeedModule = ps.colorBySpeed;
        HashModule(ref hash, 9, colorBySpeedModule.enabled);
        if (colorBySpeedModule.enabled)
        {
            AddToHash(ref hash, colorBySpeedModule.color);
            AddToHash(ref hash, colorBySpeedModule.range);
        }

        var sizeOverLifetimeModule = ps.sizeOverLifetime;
        HashModule(ref hash, 10, sizeOverLifetimeModule.enabled);
        if (sizeOverLifetimeModule.enabled)
        {
            AddToHash(ref hash, sizeOverLifetimeModule.size);
            AddToHash(ref hash, sizeOverLifetimeModule.sizeMultiplier);
            AddToHash(ref hash, sizeOverLifetimeModule.x);
            AddToHash(ref hash, sizeOverLifetimeModule.xMultiplier);
            AddToHash(ref hash, sizeOverLifetimeModule.y);
            AddToHash(ref hash, sizeOverLifetimeModule.yMultiplier);
            AddToHash(ref hash, sizeOverLifetimeModule.z);
            AddToHash(ref hash, sizeOverLifetimeModule.zMultiplier);
            AddToHash(ref hash, sizeOverLifetimeModule.separateAxes);
        }

        var sizeBySpeedModule = ps.sizeBySpeed;
        HashModule(ref hash, 11, sizeBySpeedModule.enabled);
        if (sizeBySpeedModule.enabled)
        {
            AddToHash(ref hash, sizeBySpeedModule.size);
            AddToHash(ref hash, sizeBySpeedModule.sizeMultiplier);
            AddToHash(ref hash, sizeBySpeedModule.x);
            AddToHash(ref hash, sizeBySpeedModule.xMultiplier);
            AddToHash(ref hash, sizeBySpeedModule.y);
            AddToHash(ref hash, sizeBySpeedModule.yMultiplier);
            AddToHash(ref hash, sizeBySpeedModule.z);
            AddToHash(ref hash, sizeBySpeedModule.zMultiplier);
            AddToHash(ref hash, sizeBySpeedModule.separateAxes);
            AddToHash(ref hash, sizeBySpeedModule.range);
        }

        var rotationOverLifetimeModule = ps.rotationOverLifetime;
        HashModule(ref hash, 12, rotationOverLifetimeModule.enabled);
        if (rotationOverLifetimeModule.enabled)
        {
            AddToHash(ref hash, rotationOverLifetimeModule.x);
            AddToHash(ref hash, rotationOverLifetimeModule.y);
            AddToHash(ref hash, rotationOverLifetimeModule.z);
            AddToHash(ref hash, rotationOverLifetimeModule.xMultiplier);
            AddToHash(ref hash, rotationOverLifetimeModule.yMultiplier);
            AddToHash(ref hash, rotationOverLifetimeModule.zMultiplier);
            AddToHash(ref hash, rotationOverLifetimeModule.separateAxes);
        }

        var rotationBySpeedModule = ps.rotationBySpeed;
        HashModule(ref hash, 13, rotationBySpeedModule.enabled);
        if (rotationBySpeedModule.enabled)
        {
            AddToHash(ref hash, rotationBySpeedModule.x);
            AddToHash(ref hash, rotationBySpeedModule.y);
            AddToHash(ref hash, rotationBySpeedModule.z);
            AddToHash(ref hash, rotationBySpeedModule.xMultiplier);
            AddToHash(ref hash, rotationBySpeedModule.yMultiplier);
            AddToHash(ref hash, rotationBySpeedModule.zMultiplier);
            AddToHash(ref hash, rotationBySpeedModule.separateAxes);
            AddToHash(ref hash, rotationBySpeedModule.range);
        }

        var externalForcesModule = ps.externalForces;
        HashModule(ref hash, 14, externalForcesModule.enabled);
        if (externalForcesModule.enabled)
        {
            AddToHash(ref hash, externalForcesModule.multiplier);
            AddToHash(ref hash, externalForcesModule.multiplierCurve);
            AddToHash(ref hash, (int)externalForcesModule.influenceFilter);
            AddToHash(ref hash, externalForcesModule.influenceMask);
        }

        var noiseModule = ps.noise;
        HashModule(ref hash, 15, noiseModule.enabled);
        if (noiseModule.enabled)
        {
            AddToHash(ref hash, noiseModule.separateAxes);
            AddToHash(ref hash, noiseModule.strength);
            AddToHash(ref hash, noiseModule.strengthMultiplier);
            AddToHash(ref hash, noiseModule.strengthX);
            AddToHash(ref hash, noiseModule.strengthXMultiplier);
            AddToHash(ref hash, noiseModule.strengthY);
            AddToHash(ref hash, noiseModule.strengthYMultiplier);
            AddToHash(ref hash, noiseModule.strengthZ);
            AddToHash(ref hash, noiseModule.strengthZMultiplier);
            AddToHash(ref hash, noiseModule.frequency);
            AddToHash(ref hash, noiseModule.damping);
            AddToHash(ref hash, noiseModule.octaveCount);
            AddToHash(ref hash, noiseModule.octaveMultiplier);
            AddToHash(ref hash, noiseModule.octaveScale);
            AddToHash(ref hash, (int)noiseModule.quality);
            AddToHash(ref hash, noiseModule.scrollSpeed);
            AddToHash(ref hash, noiseModule.scrollSpeedMultiplier);
            AddToHash(ref hash, noiseModule.remapEnabled);
            AddToHash(ref hash, noiseModule.remap);
            AddToHash(ref hash, noiseModule.remapMultiplier);
            AddToHash(ref hash, noiseModule.remapX);
            AddToHash(ref hash, noiseModule.remapY);
            AddToHash(ref hash, noiseModule.remapZ);
            AddToHash(ref hash, noiseModule.remapXMultiplier);
            AddToHash(ref hash, noiseModule.remapYMultiplier);
            AddToHash(ref hash, noiseModule.remapZMultiplier);
            AddToHash(ref hash, noiseModule.positionAmount);
            AddToHash(ref hash, noiseModule.rotationAmount);
            AddToHash(ref hash, noiseModule.sizeAmount);
        } 

        var collisionModule = ps.collision;
        HashModule(ref hash, 16, collisionModule.enabled);
        if (collisionModule.enabled)
        {
            AddToHash(ref hash, (int)collisionModule.type);
            AddToHash(ref hash, (int)collisionModule.mode);
            AddToHash(ref hash, collisionModule.dampen);
            AddToHash(ref hash, collisionModule.dampenMultiplier);
            AddToHash(ref hash, collisionModule.bounce);
            AddToHash(ref hash, collisionModule.bounceMultiplier);
            AddToHash(ref hash, collisionModule.lifetimeLoss);
            AddToHash(ref hash, collisionModule.lifetimeLossMultiplier);
            AddToHash(ref hash, collisionModule.minKillSpeed);
            AddToHash(ref hash, collisionModule.maxKillSpeed);
            AddToHash(ref hash, collisionModule.collidesWith);
            AddToHash(ref hash, collisionModule.enableDynamicColliders);
            AddToHash(ref hash, collisionModule.maxCollisionShapes);
            AddToHash(ref hash, (int)collisionModule.quality);
            AddToHash(ref hash, collisionModule.voxelSize);
            AddToHash(ref hash, collisionModule.radiusScale);
            AddToHash(ref hash, collisionModule.sendCollisionMessages);
            AddToHash(ref hash, collisionModule.colliderForce);
            AddToHash(ref hash, collisionModule.multiplyColliderForceByCollisionAngle);
            AddToHash(ref hash, collisionModule.multiplyColliderForceByParticleSpeed);
            AddToHash(ref hash, collisionModule.multiplyColliderForceByParticleSize);
        }

        var triggerModule = ps.trigger;
        HashModule(ref hash, 17, triggerModule.enabled);
        if (triggerModule.enabled)
        {
            AddToHash(ref hash, (int)triggerModule.inside);
            AddToHash(ref hash, (int)triggerModule.outside);
            AddToHash(ref hash, (int)triggerModule.enter);
            AddToHash(ref hash, (int)triggerModule.exit);
            AddToHash(ref hash, (int)triggerModule.colliderQueryMode);
            AddToHash(ref hash, triggerModule.radiusScale);
        }

        var subEmittersModule = ps.subEmitters;
        HashModule(ref hash, 18, subEmittersModule.enabled);
        if (subEmittersModule.enabled)
        {
            AddToHash(ref hash, subEmittersModule.subEmittersCount);
            for (int i = 0; i < subEmittersModule.subEmittersCount; i++)
            {
                var subEmitter = subEmittersModule.GetSubEmitterSystem(i);
                if (subEmitter != null)
                {
                    AddToHash(ref hash, ParticleSystemHasher.ComputeParticleHash(subEmitter));
                }
                else
                {
                    AddToHash(ref hash, 0); // 如果子发射器为空，使用0表示
                }
                var probability = subEmittersModule.GetSubEmitterEmitProbability(i);
                AddToHash(ref hash, probability);
                var properties = subEmittersModule.GetSubEmitterProperties(i);
                AddToHash(ref hash, (int)properties);
                var type = subEmittersModule.GetSubEmitterType(i);
                AddToHash(ref hash, (int)type);
            }
        }

        var textureSheetAnimationModule = ps.textureSheetAnimation;
        HashModule(ref hash, 19, textureSheetAnimationModule.enabled);
        if (textureSheetAnimationModule.enabled)
        {
            AddToHash(ref hash, (int)textureSheetAnimationModule.mode);
            AddToHash(ref hash, (int)textureSheetAnimationModule.timeMode);
            AddToHash(ref hash, textureSheetAnimationModule.fps);
            AddToHash(ref hash, textureSheetAnimationModule.numTilesX);
            AddToHash(ref hash, textureSheetAnimationModule.numTilesY);
            AddToHash(ref hash, (int)textureSheetAnimationModule.animation);
            AddToHash(ref hash, (int)textureSheetAnimationModule.rowMode);
            AddToHash(ref hash, textureSheetAnimationModule.frameOverTime);
            AddToHash(ref hash, textureSheetAnimationModule.frameOverTimeMultiplier);
            AddToHash(ref hash, textureSheetAnimationModule.startFrame);
            AddToHash(ref hash, textureSheetAnimationModule.startFrameMultiplier);
            AddToHash(ref hash, textureSheetAnimationModule.cycleCount);
            AddToHash(ref hash, textureSheetAnimationModule.rowIndex);
            AddToHash(ref hash, (int)textureSheetAnimationModule.uvChannelMask);
            AddToHash(ref hash, textureSheetAnimationModule.speedRange);
        }

        var lightsModule = ps.lights;
        HashModule(ref hash, 20, lightsModule.enabled);
        if (lightsModule.enabled)
        {
            AddToHash(ref hash, lightsModule.ratio);
            AddToHash(ref hash, lightsModule.useRandomDistribution);
            AddToHash(ref hash, lightsModule.light ? lightsModule.light.GetInstanceID() : 0);
            AddToHash(ref hash, lightsModule.useParticleColor);
            AddToHash(ref hash, lightsModule.sizeAffectsRange);
            AddToHash(ref hash, lightsModule.alphaAffectsIntensity);
            AddToHash(ref hash, lightsModule.range);
            AddToHash(ref hash, lightsModule.rangeMultiplier);
            AddToHash(ref hash, lightsModule.intensity);
            AddToHash(ref hash, lightsModule.intensityMultiplier);
            AddToHash(ref hash, lightsModule.maxLights);
        }

        var trailsModule = ps.trails;
        HashModule(ref hash, 21, trailsModule.enabled);
        if (trailsModule.enabled)
        {
            AddToHash(ref hash, (int)trailsModule.mode);
            AddToHash(ref hash, trailsModule.ratio);
            AddToHash(ref hash, trailsModule.lifetime);
            AddToHash(ref hash, trailsModule.lifetimeMultiplier);
            AddToHash(ref hash, trailsModule.minVertexDistance);
            AddToHash(ref hash, (int)trailsModule.textureMode);
            AddToHash(ref hash, trailsModule.worldSpace);
            AddToHash(ref hash, trailsModule.dieWithParticles);
            AddToHash(ref hash, trailsModule.sizeAffectsWidth);
            AddToHash(ref hash, trailsModule.sizeAffectsLifetime);
            AddToHash(ref hash, trailsModule.inheritParticleColor);
            AddToHash(ref hash, trailsModule.colorOverLifetime);
            AddToHash(ref hash, trailsModule.widthOverTrail);
            AddToHash(ref hash, trailsModule.widthOverTrailMultiplier);
            AddToHash(ref hash, trailsModule.colorOverTrail);
            AddToHash(ref hash, trailsModule.generateLightingData);
            AddToHash(ref hash, trailsModule.ribbonCount);
            AddToHash(ref hash, trailsModule.shadowBias);
            AddToHash(ref hash, trailsModule.splitSubEmitterRibbons);
            AddToHash(ref hash, trailsModule.attachRibbonsToTransform);
        }

        var customDataModule = ps.customData;
        HashModule(ref hash, 22, customDataModule.enabled);
        if (customDataModule.enabled)
        {
            var mode_1 = ps.customData.GetMode(ParticleSystemCustomData.Custom1);
            AddToHash(ref hash, (int)mode_1);
            var color_1 = ps.customData.GetColor(ParticleSystemCustomData.Custom1);
            AddToHash(ref hash, color_1);
            var vectorComponentCount_1 = ps.customData.GetVectorComponentCount(ParticleSystemCustomData.Custom1);
            AddToHash(ref hash, vectorComponentCount_1);
            for (int i = 0; i < vectorComponentCount_1; i++)
            {
                var vector_1 = ps.customData.GetVector(ParticleSystemCustomData.Custom1, i);
                AddToHash(ref hash, vector_1);
            }

            var mode_2 = ps.customData.GetMode(ParticleSystemCustomData.Custom2);
            AddToHash(ref hash, (int)mode_2);
            var color_2 = ps.customData.GetColor(ParticleSystemCustomData.Custom2);
            AddToHash(ref hash, color_2);
            var vectorComponentCount_2 = ps.customData.GetVectorComponentCount(ParticleSystemCustomData.Custom2);
            AddToHash(ref hash, vectorComponentCount_2);
            for (int i = 0; i < vectorComponentCount_2; i++)
            {
                var vector_2 = ps.customData.GetVector(ParticleSystemCustomData.Custom2, i);
                AddToHash(ref hash, vector_2);
            }
        }

        // 渲染模块
        var renderer = ps.GetComponent<ParticleSystemRenderer>();
        HashModule(ref hash, 23, renderer.enabled);

        // 不论渲染模块是否启用，都需要计算哈希
        AddToHash(ref hash, (int)renderer.renderMode);
        AddToHash(ref hash, (int)renderer.alignment);
        AddToHash(ref hash, (int)renderer.sortMode);
        AddToHash(ref hash, renderer.lengthScale);
        AddToHash(ref hash, renderer.velocityScale);
        AddToHash(ref hash, renderer.cameraVelocityScale);
        AddToHash(ref hash, renderer.normalDirection);
        AddToHash(ref hash, renderer.shadowBias);
        AddToHash(ref hash, renderer.sortingFudge);
        AddToHash(ref hash, renderer.minParticleSize);
        AddToHash(ref hash, renderer.maxParticleSize);
        AddToHash(ref hash, renderer.pivot);
        AddToHash(ref hash, renderer.flip);
        AddToHash(ref hash, (int)renderer.maskInteraction);
        AddToHash(ref hash, renderer.trailMaterial ? renderer.trailMaterial.GetInstanceID() : 0);
        AddToHash(ref hash, renderer.enableGPUInstancing);
        AddToHash(ref hash, renderer.allowRoll);
        AddToHash(ref hash, renderer.freeformStretching);
        AddToHash(ref hash, renderer.rotateWithStretchDirection);
        AddToHash(ref hash, renderer.mesh ? renderer.mesh.GetInstanceID() : 0);
        AddToHash(ref hash, renderer.sharedMaterial ? renderer.sharedMaterial.GetInstanceID() : 0);
        AddToHash(ref hash, renderer.sortingOrder);
        AddToHash(ref hash, renderer.rendererPriority);
        AddToHash(ref hash, renderer.renderingLayerMask);

        ParticleSystemVertexStream.Clear();
        renderer.GetActiveVertexStreams(ParticleSystemVertexStream);
        foreach (var stream in ParticleSystemVertexStream)
        {
            AddToHash(ref hash, (int)stream);
        }

        return hash;
    }

    
    static List<ParticleSystemVertexStream> ParticleSystemVertexStream = new List<ParticleSystemVertexStream>(10); // 预分配10个，一般只会用到4-6个

    // 浮点数量化方法（避免浮点精度问题）
    private static int QuantizeFloat(float value)
    {
        return (int)(value * FloatPrecision + 0.5f);
        // return Mathf.RoundToInt(value * FloatPrecision);
    }

    // 添加模块的基本信息到哈希
    private static void HashModule(ref ulong hash, int moduleIndex, bool enabled)
    {
        // 模块索引和启用状态
        AddToHash(ref hash, moduleIndex);
        AddToHash(ref hash, enabled ? 1 : 0);
    }

    // 添加值到哈希（使用FNV-1a算法）
    private static void AddToHash(ref ulong hash, Color value)
    {
        AddToHash(ref hash, value.r);
        AddToHash(ref hash, value.g);
        AddToHash(ref hash, value.b);
        AddToHash(ref hash, value.a);
    }

    // 添加值到哈希（使用FNV-1a算法）
    private static void AddToHash(ref ulong hash, Vector3 value)
    {
        AddToHash(ref hash, value.x);
        AddToHash(ref hash, value.y);
        AddToHash(ref hash, value.z);
    }

    // 添加值到哈希（使用FNV-1a算法）
    private static void AddToHash(ref ulong hash, Vector2 value)
    {
        AddToHash(ref hash, value.x);
        AddToHash(ref hash, value.y);
    }

    // 添加值到哈希（使用FNV-1a算法）
    private static void AddToHash(ref ulong hash, float value)
    {
        // 将浮点数转换为整数
        int intValue = QuantizeFloat(value);
        AddToHash(ref hash, intValue);
    }

    // 添加值到哈希（使用FNV-1a算法）
    private static void AddToHash(ref ulong hash, int value)
    {
        // 将32位整数拆分为4个字节处理
        for (int i = 0; i < 4; i++)
        {
            byte b = (byte)(value >> (i * 8));
            hash = (hash ^ b) * FNV64Prime;
        }
    }

    // 添加值到哈希（使用FNV-1a算法）
    private static void AddToHash(ref ulong hash, MinMaxCurve value)
    {
        AddToHash(ref hash, (int)value.mode);
        if( value.mode == ParticleSystemCurveMode.Constant)
        {
            AddToHash(ref hash, value.constant);
        }
        else if (value.mode == ParticleSystemCurveMode.TwoConstants)
        {
            AddToHash(ref hash, value.constantMin);
            AddToHash(ref hash, value.constantMax);
        }
        else if (value.mode == ParticleSystemCurveMode.Curve)
        {
            AddToHash(ref hash, value.curve);
        }
        else if (value.mode == ParticleSystemCurveMode.TwoCurves)
        {
            AddToHash(ref hash, value.curveMin);
            AddToHash(ref hash, value.curveMax);
        }
    }

    // 添加值到哈希（使用FNV-1a算法）
    private static void AddToHash(ref ulong hash, AnimationCurve value)
    {
        if (value == null)
        {
            return;
        }

        AddToHash(ref hash, value.length);
        foreach (var key in value.keys)
        {
            AddToHash(ref hash, key.time);
            AddToHash(ref hash, key.value);
            AddToHash(ref hash, key.inTangent);
            AddToHash(ref hash, key.outTangent);
            AddToHash(ref hash, (int)key.weightedMode);
            AddToHash(ref hash, key.inWeight);
            AddToHash(ref hash, key.outWeight);
            AddToHash(ref hash, key.tangentMode);
        }
        AddToHash(ref hash, (int)value.preWrapMode);
        AddToHash(ref hash, (int)value.postWrapMode);
    }


    // 添加值到哈希（使用FNV-1a算法）
    private static void AddToHash(ref ulong hash, MinMaxGradient value)
    {
        AddToHash(ref hash, (int)value.mode);
        AddToHash(ref hash, value.colorMin);
        AddToHash(ref hash, value.colorMax);
        AddToHash(ref hash, value.gradientMin);
        AddToHash(ref hash, value.gradientMax);
    }

    // 添加值到哈希（使用FNV-1a算法）
    private static void AddToHash(ref ulong hash, Gradient value)
    {
        if (value == null)
        {
            return;
        }

        AddToHash(ref hash, (int)value.mode);
        foreach (var key in value.colorKeys)
        {
            AddToHash(ref hash, key.time);
            AddToHash(ref hash, key.color);
        }
        foreach (var key in value.alphaKeys)
        {
            AddToHash(ref hash, key.time);
            AddToHash(ref hash, key.alpha);
        }
    }
    
    // 添加布尔值到哈希（使用FNV-1a算法）
    private static void AddToHash(ref ulong hash, bool value)
    {
        // 将布尔值转换为整数
        int intValue = value ? 1 : 0;
        AddToHash(ref hash, intValue);
    }

    // 添加64位值到哈希
    private static void AddToHash(ref ulong hash, ulong value)
    {
        // 将64位整数拆分为8个字节处理
        for (int i = 0; i < 8; i++)
        {
            byte b = (byte)(value >> (i * 8));
            hash = (hash ^ b) * FNV64Prime;
        }
    }
}